﻿using System;
using System.Buffers;
using System.IO;
using System.IO.Pipes;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Channels;
using System.Threading.Tasks;
using UMC.Net;

namespace UMC.ITME
{

    class HttpBridgeMime : HttpMime, INetBridgeReader
    {

        public override bool IsSSL => true;
        public override StringValue Finger => _finger;//StringValue.Empty;
        DataWriter client;
        public HttpBridgeMime(DataWriter client, StringValue value)
        {
            this.client = client;
            this.pid = this.GetHashCode();

            var bytes = value.Span;

            this._host = value.Slice(1, bytes[0]); ;
            var size = bytes[0] + 1;

            this._finger = value.Slice(size + 1, bytes[size]);
            size += bytes[size] + 1;
            this._RemoteAddress = value.Slice(size + 1, bytes[size]);

        }
        int pid;
        StringValue _host, _finger;
        public override StringValue Host => _host;
        StringValue _RemoteAddress = StringValue.Empty;
        public override StringValue RemoteIpAddress => _RemoteAddress;

        public override int Id => pid;
        bool _IsOver;
        public bool IsOver => _IsOver;


        bool isDispose = false;
        public override void Dispose()
        {
            if (isDispose == false)
            {
                _IsOver = true;
                this.channel?.Writer?.TryComplete();
                this.MimeMemory.Dispose();
                isDispose = true;
                if (this.Request is IDisposable)
                {
                    ((IDisposable)this.Request).Dispose();
                }

                if (_buffer.Length > 0)
                {
                    _bufferSize = 0;
                    ArrayPool<byte>.Shared.Return(_buffer);
                }
                _buffer = Array.Empty<byte>();
            }
        }



        protected override void Send(byte[] buffer, int offset, int count)
        {
            if (count > 0)
            {
                client.Write(buffer.AsSpan(offset, count));//, offset, count);
                client.Flush();
            }
        }

        byte[] _buffer = Array.Empty<byte>();
        int _bufferSize = 0;
        public void Read(byte[] buffer, int offset, int size)
        {
            this.ActiveTime = UMC.Data.Utility.TimeSpan();

            if (_bufferSize > 0)
            {
                if (_bufferSize + size > _buffer.Length)
                {
                    var bs = ArrayPool<byte>.Shared.Rent(_bufferSize + size);
                    Array.Copy(_buffer, 0, bs, 0, _bufferSize);
                    ArrayPool<byte>.Shared.Return(_buffer);
                    _buffer = bs;
                }

                Array.Copy(buffer, offset, _buffer, _bufferSize, size);
                buffer = _buffer;
                offset = 0;
                size += _bufferSize;
                _bufferSize = 0;

            }

            if (this.Request == null || this.Request?.IsMimeFinish == true)
            {
                var netLoger = new HttpLoger(new ByteChunk());
                netLoger.Write(HttpLogerType.IP, this._RemoteAddress);
                this.Request = new HttpMimeRequest(this, this.MimeMemory, netLoger);
            }
            var req = this.Request;
            var len = req.Receive(buffer, offset, size);


            if (len > 0)
            {
                if (_buffer.Length < len)
                {
                    var bs = ArrayPool<byte>.Shared.Rent(len);
                    if (_buffer.Length > 0)
                    {
                        ArrayPool<byte>.Shared.Return(_buffer);
                    }
                    _buffer = bs;

                }
                Array.Copy(buffer, offset + size - len, _buffer, 0, len);
                _bufferSize = len;

            }
            else
            {
                _bufferSize = 0;
            }

        }
        Channel<Tuple<long, int>> channel;
        public void Receive(byte[] buffer, int offset, int size)
        {
            if (size == 0)
            {

                this.channel.Writer.TryWrite(Tuple.Create(0L, 0));
                _IsOver = true;
            }
            else
            {

                if (this.channel == null)
                {
                    this.channel = Channel.CreateUnbounded<Tuple<long, int>>(new UnboundedChannelOptions
                    {
                        SingleReader = true,
                        SingleWriter = true,
                        AllowSynchronousContinuations = false
                    });
                    Receive(channel.Reader);
                }
                this.channel.Writer.TryWrite(Tuple.Create(NetCache.Share.Append(buffer, offset, size), size));
                // _OutputSize += size;
            }
        }

        async void Receive(ChannelReader<Tuple<long, int>> reader)
        {
            var buffer = ArrayPool<byte>.Shared.Rent(400);
            try
            {
                while (await reader.WaitToReadAsync())
                {
                    if (reader.TryRead(out var result))
                    {
                        if (result.Item2 > 0)
                        {
                            using (var view = NetCache.Share.CreateAccessor(result.Item1, result.Item2))
                            {

                                int size = 0;
                                while ((size = await view.ReadAsync(buffer, 0, buffer.Length)) > 0)
                                {
                                    // view.WriteAsync
                                    this.Read(buffer, 0, size);
                                }
                            }
                        }
                        else
                        {
                            this.Dispose();
                        }

                    }
                }
                this.Dispose();
            }
            finally
            {

                ArrayPool<byte>.Shared.Return(buffer);
            }
        }


        // public void Receive(NetCache cache, long offset, int size)
        // {

        //     var buffer = ArrayPool<byte>.Shared.Rent(500);
        //     try
        //     {

        //         using (var view = NetCache.Share.CreateAccessor(offset, size))
        //         {
        //             int len = 0;
        //             while ((len = view.Read(buffer, 0, buffer.Length)) > 0)
        //             {
        //                 Receive(buffer, 0, len);
        //             }
        //         }
        //     }
        //     finally
        //     {
        //         ArrayPool<byte>.Shared.Return(buffer);
        //     }
        // }
    }
}

